﻿#include "stdafx.h"
#if _QG_USE_DX9
#include "qg/qgasv.h"
#include "qg_dx9_dev.h"
#include "qg_dx9_cnv.h"
#include "qg_dx9_mtrl.h"
#include "qg_trace.h"

//
bool DX9Device::_BindShader() throw()
{
	if (_sprn->bd_shd)
	{
		_sprn->bd_shd = false;
		if (!_CommitShader())
			return false;
	}

	if (_sprn->m_shd)
	{
		DX9Shader* p = (DX9Shader*)_sprn->m_shd;
		p->_ProcessMap();
	}

	return true;
}

//
bool DX9Device::_CommitShader() throw()
{
	if (!_sprn->m_shd)
	{
		if (_vprn.m_shd_ps != NULL)
		{
			_vprn.m_shd_ps = NULL;
			_device->SetPixelShader(NULL);
		}

		if (_vprn.m_shd_vs != NULL)
		{
			_vprn.m_shd_vs = NULL;
			_device->SetVertexShader(NULL);
		}
	}
	else
	{
		DX9Shader* s = (DX9Shader*)_sprn->m_shd;

		if (!s->_linked)
		{
			// 링크 안된 넘이다
			kobj_tracet(KLV_WARNING, KERR_NOREF, "no linked shader specified. will be linking instead.");
			kobj_tracef(KLV_WARNING, "target shader is '%s'", s->_key.Data());

			if (!s->Link())
				return false;
		}

		if (_vprn.m_shd_ps != s->_ps)
		{
			_vprn.m_shd_ps = s->_ps;
			_device->SetPixelShader(s->_ps);
		}

		if (_vprn.m_shd_vs != s->_vs)
		{
			_vprn.m_shd_vs = s->_vs;
			_device->SetVertexShader(s->_vs);
		}
	}

	return true;
}

//
qgShader* DX9Device::_RoamImplShader(const char* name)
{
	DX9Shader* shd = kobj_create(DX9Shader);

	if (name)
		shd->_key = name;
	else
	{
		ksize_t n = k_nth();
		shd->_key.Format("dx9_shader_%Lu", n);
	}

	return shd;
}


//////////////////////////////////////////////////////////////////////////
// 세이더
KIMPL_SUBCLASS(DX9Shader, "DX9Shader");

// include 구문 처리용, 디바이스 마운트에서 파일 가져오기
class DX9Shader::InclUnit : public ID3DXInclude
{
private:
	DX9Device* _dev;

public:
	InclUnit(DX9Device* dev)
		: _dev(dev)
	{
	}

	K_OVR HRESULT WINAPI Open(D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
	{
		// D3DXINC_LOCAL -> ""로 묶음
		// D3DXINC_SYSTEM -> <>로 묶음
		// ... 별 차이 없으므로 걍 똑같이 함

		kint size;
		kpointer ptr = _dev->FileRead(pFileName, &size);

		if (!ptr)
			return E_FAIL;
		else
		{
			*ppData = ptr;
			*pBytes = (UINT)size;

			return S_OK;
		}
	}

	K_OVR HRESULT WINAPI Close(LPCVOID pData)
	{
		k_delete(pData);

		return S_OK;
	}
};

//
void DX9Shader::TypeInitialize(kType type)
{
	// 해시를 위해서
	_InitAutoFunc();
}

//
DX9Shader::DX9Shader() throw()
{
}

//
DX9Shader::~DX9Shader() throw()
{
	_RemoveHandle((IUnknown**)&_ps, &_dhp, false);
	_RemoveHandle((IUnknown**)&_vs, &_dhv, false);
}

//
void DX9Shader::_PrintError(LPD3DXBUFFER errs, kint errcode, const char* target) throw()
{
	kobj_tracef(KLV_ERROR, "%s with shader '%s'", k_errmsg(errcode), target);

	if (KLV_ERROR >= k_getlv() && errs)
	{
		if (errs->GetBufferSize() > 0)
		{
			const char* psz = (const char*)errs->GetBufferPointer();
			k_mesg(psz);
		}
	}
}

//
bool DX9Shader::_CreatePixelShader(kconstpointer data, kint size, CONST D3DXMACRO* mcs, InclUnit& inc, DWORD dw) throw()
{
	DX9Device* dev = (DX9Device*)_dev;
	const char* target = dev->GetProfilePs();

	LPD3DXBUFFER blob;
	LPD3DXBUFFER errs;
	ID3DXConstantTable* ctbl;
	HRESULT hr;
	kint ret;

	//
	_RemoveHandle((IUnknown**)&_ps, &_dhp, true);

	//
	hr = DX9Device::DXCOMPILESHADER((LPCSTR)data, size, mcs, &inc, "PS",
		target, dw, &blob, &errs, &ctbl);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		kpointer blobdata = blob->GetBufferPointer();

		hr = dev->GetDevice()->CreatePixelShader((const DWORD*)blobdata, &_ps);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;

			_dhp = _AnalyzeData(QGSHADER_PS, ctbl);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_PrintError(errs, ret, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
bool DX9Shader::_CreateVertexShader(kconstpointer data, kint size, CONST D3DXMACRO* mcs, InclUnit& inc, DWORD dw) throw()
{
	DX9Device* dev = (DX9Device*)_dev;
	const char* target = dev->GetProfileVs();

	LPD3DXBUFFER blob;
	LPD3DXBUFFER errs;
	ID3DXConstantTable* ctbl;
	HRESULT hr;
	kint ret;

	//
	_RemoveHandle((IUnknown**)&_vs, &_dhv, true);

	//
	hr = DX9Device::DXCOMPILESHADER((LPCSTR)data, size, mcs, &inc, "VS",
		target, dw, &blob, &errs, &ctbl);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		kpointer blobdata = blob->GetBufferPointer();

		hr = dev->GetDevice()->CreateVertexShader((const DWORD*)blobdata, &_vs);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;

			_dhv = _AnalyzeData(QGSHADER_VS, ctbl);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_PrintError(errs, ret, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
D3DXMACRO* DX9Shader::_MakeDefinition(ConditionArray& conds)
{
	if (conds.IsEmpty())
		return NULL;

	D3DXMACRO* mcs = k_new(conds.Count() + 1, D3DXMACRO);
	D3DXMACRO* p = mcs;

	kForEach(auto& str, conds)
	{
		p->Name = str.Data();
		p->Definition = "1";	// 문자열은 상수니깐 설정해도 괜찮다.
		p++;
	}

	// 마지막은 NULL로 끝나지 않으면 안된다고 하더군
	p->Name = NULL;
	p->Definition = NULL;

	return mcs;
}

//
DWORD DX9Shader::_MakeFlags(kint v)
{
	DWORD dw = D3DXSHADER_PACKMATRIX_ROWMAJOR;

#if _SB_DEBUG_
	dw |= D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION;
#else
	dw |= D3DXSHADER_OPTIMIZATION_LEVEL3;
#endif

#if 0
	if (v & 0x0001)
		dw |= D3DXSHADER_PACKMATRIX_COLUMNMAJOR;

	if (v & 0x0002)
		dw |= D3DXSHADER_PACKMATRIX_ROWMAJOR;
#endif

	if (v & 0x0004)
		dw |= D3DXSHADER_PARTIALPRECISION;

	return dw;
}

//
DX9Shader::MapHandle* DX9Shader::_AnalyzeData(qgShaderType type, ID3DXConstantTable* ctbl) throw()
{
	D3DXCONSTANTTABLE_DESC dtd;

	if (FAILED(ctbl->GetDesc(&dtd)))
		return NULL;

	kint cnt = (kint)dtd.Constants;

	MapHandle* h = new MapHandle();
	h->ref = 1;
	h->ctbl = ctbl;
	h->vars.Resize(cnt);

	for (kint i = 0; i < cnt; i++)
	{
		D3DXHANDLE p = ctbl->GetConstant(NULL, i);

		D3DXCONSTANT_DESC cd;
		UINT nd = 1;
		ctbl->GetConstantDesc(p, &cd, &nd);

		qgShaderData datatype = _ShaderTypeToShaderData(cd);
		if (datatype == QGSHDD_UNKNOWN)
		{
			kobj_tracet(KLV_WARNING, KERR_SYNTAX, "no support shader constant type.");
			kobj_tracef(KLV_WARNING, "type name '%s' of shader '%s'", cd.Name, _key.Data());
		}

		qgShaderVar& v = h->vars[i];
		v.name = cd.Name;
		v.hash = v.name.Hash(true);
		v.offset = K_CAST_PTR_TO_PUINT(p);
		v.size = (kint)cd.Bytes;
		v.data = datatype;
		v.ptr = ctbl;
		_MatchAutoFunc(v);
	}

	return h;
}

//
void DX9Shader::_RemoveHandle(IUnknown** unk, MapHandle** pmh, bool reset)
{
	if (*unk)
	{
		(*unk)->Release();

		if (reset)
			(*unk) = NULL;
	}

	//
	MapHandle* h = *pmh;

	if (!h || k_atomdec(&h->ref) != 0)
	{
		if (reset)
			(*pmh) = NULL;
	}
	else
	{
		delete h;

		if (reset)
			(*pmh) = NULL;
	}
}

//
bool DX9Shader::_InternalBindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw()
{
	D3DXMACRO* mcs = _MakeDefinition(_conds);
	InclUnit iu((DX9Device*)_dev);
	DWORD dw = _MakeFlags(flags);

	//
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			ret = _CreatePixelShader(data, size, mcs, iu, dw);
			K_UFLAG(&_insts, QGSHDI_PS, ret);
			break;

		case QGSHADER_VS:
			ret = _CreateVertexShader(data, size, mcs, iu, dw);
			K_UFLAG(&_insts, QGSHDI_VS, ret);
			break;

		default:
			ret = false;
			break;
	}

	//
	k_delete(mcs);

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool DX9Shader::BindFile(qgShaderType type, const char* filename, kint flags) throw()
{
	kint readsize;
	tpl::ScopedMemPtrAlloc<kpointer> readdata(_dev->FileRead(filename, &readsize));

	k_return_value_if_fail(readdata, false);

	return _InternalBindData(type, readdata, readsize, flags);
}

//
bool DX9Shader::BindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw()
{
	k_return_value_if_fail(data && size > 0, false);

	return _InternalBindData(type, data, size, flags);
}

//
bool DX9Shader::BindShader(qgShaderType type, const char* name) throw()
{
	k_return_value_if_fail(name, 0);

	// 찾는다
	KeyType key(name);

	DX9Shader* shd = (DX9Shader*)((DX9Device*)_dev)->_RoamFind(QGROAM_MTL_2, &key);

	k_return_value_if_fail(shd, 0);

	// 찾는게 있으면 세이더를 참조해둔다
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			_RemoveHandle((IUnknown**)&_ps, &_dhp, true);

			if (!shd->_ps)
				ret = false;
			else
			{
				_ps = shd->_ps;
				_ps->AddRef();

				_dhp = shd->_dhp;
				k_atominc(&_dhp->ref);

				ret = true;
			}

			K_UFLAG(&_insts, QGSHDI_PS, ret);
			break;

		case QGSHADER_VS:
			_RemoveHandle((IUnknown**)&_vs, &_dhv, true);

			if (!shd->_vs)
				ret = false;
			else
			{
				_vs = shd->_vs;
				_vs->AddRef();

				_dhv = shd->_dhv;
				k_atominc(&_dhv->ref);

				ret = true;
			}

			K_UFLAG(&_insts, QGSHDI_VS, ret);
			break;

		default:
			ret = false;
			break;
	}

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool DX9Shader::Link()
{
	k_return_value_if_ok(_linked, true);

	if (!K_OFLAG(_insts, QGSHDI_PS | QGSHDI_VS))
	{
		// DX9은 ps, vs 둘다 있어야 한다
		kobj_tracep(KLV_ERROR, KERR_NOREF);
		return false;
	}

	_linked = true;

	return true;
}

//
qgShaderVar* DX9Shader::GetVars(kint stage, kint* count)
{
	// 이렇게 해도 될것인가! 속도는 우짜지!
	// 그전에 전개 논리에 문제가 있음
	kint cntps = _dhp ? 1 : 0;
	kint cntvs = _dhv ? 1 : 0;

	tpl::Array<qgShaderVar>* ptr;

	if (stage < 0)
		ptr = NULL;
	else if (stage < cntps)
		ptr = &_dhp->vars;
	else if (stage < (cntps + cntvs))
		ptr = &_dhp->vars;
	else
		ptr = NULL;

	if (!ptr)
	{
		if (count)
			*count = 0;
		return NULL;
	}
	else
	{
		if (count)
			*count = ptr->Count();
		return ptr->Data();
	}
}

//
void DX9Shader::_ProcessHandle(MapHandle* h) throw()
{
	kForEach(qgShaderVar& v, h->vars)
	{
		// DX9는 data에 offset을 넣는다. 안넣어도 되고
		if (v.role == QGSHDR_AUTO)
			((qgShaderAutoFunc)v.intr)(_dev, (kpointer)v.offset, v);
	}

	// 사용자 지정
	if (_cbintr)
		_cbintr(h->vars.Count(), h->vars.Data(), this);
}

//
void DX9Shader::_ProcessMap() throw()
{
	if (_dhp && _dhp->vars.IsHave())
		_ProcessHandle(_dhp);

	if (_dhv && _dhv->vars.IsHave())
		_ProcessHandle(_dhv);
}

//
qgShaderData DX9Shader::_ShaderTypeToShaderData(const D3DXCONSTANT_DESC& cd)
{
	if (cd.Class == D3DXPC_OBJECT)
	{
		switch (cd.Type)
		{
			case D3DXPT_SAMPLER1D:
				return QGSHDD_SPLR_1D;

			case D3DXPT_SAMPLER2D:
				return QGSHDD_SPLR_2D;

			case D3DXPT_SAMPLER3D:
				return QGSHDD_SPLR_3D;

			case D3DXPT_SAMPLERCUBE:
				return QGSHDD_SPLR_CUBE;
		}
	}
	else
	{
		switch (cd.Type)
		{
			case D3DXPT_BOOL:
				switch (cd.Columns)
				{
					case 1:
						return QGSHDD_BYTE1;

					case 2:
						return QGSHDD_BYTE2;

					case 3:
						return QGSHDD_BYTE3;

					case 4:
						return QGSHDD_BYTE4;
				}
				break;

			case D3DXPT_INT:
				switch (cd.Columns)
				{
					case 1:
						return QGSHDD_INT1;

					case 2:
						return QGSHDD_INT2;

					case 3:
						return QGSHDD_INT3;

					case 4:
						return QGSHDD_INT4;
				}
				break;

			case D3DXPT_FLOAT:
				switch (cd.Rows)
				{
					case 1:	// 벡터 = 1x? 행렬
						switch (cd.Columns)
						{
							case 1:
								return QGSHDD_FLOAT1;

							case 2:
								return QGSHDD_FLOAT2;

							case 3:
								return QGSHDD_FLOAT3;

							case 4:
								return QGSHDD_FLOAT4;
						}
						break;

					case 2:	// 2x? 행렬
						break;

					case 3:	// 3x? 행렬
						break;

					case 4:	// 4x? 행렬
						switch (cd.Columns)
						{
							case 4:
								return QGSHDD_FLOAT16;
						}
						break;
				}
				break;
		}
	}

	return QGSHDD_UNKNOWN;
}


//////////////////////////////////////////////////////////////////////////
// 자동 세이더 변수 처리
class DX9ShaderWorker
{
	// friends 관계 때문에 namespace로 안하고 class로 함

private:
	static void OrthoProj(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.otm.ToPointer());
	}

	static void World(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.tm[QGTMS_WORLD].ToPointer());
	}

	static void View(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.tm[QGTMS_VIEW].ToPointer());
	}

	static void Proj(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.tm[QGTMS_PROJ].ToPointer());
	}

	static void ViewProj(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.vipr.ToPointer());
	}

	static void InvView(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_trfm.invv.ToPointer());
	}

	static void WorldViewProj(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)(dev->_trfm.tm[QGTMS_WORLD] * dev->_trfm.vipr).ToPointer());
	}

	static void Tex0(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex1(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex2(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex3(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void PropVec0(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		k_assert_eq(v.size, 16);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetVector(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXVECTOR4*)&dev->_vdeb.propv[0]);
	}

	static void PropVec1(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		k_assert_eq(v.size, 16);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetVector(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXVECTOR4*)&dev->_vdeb.propv[1]);
	}

	static void PropVec2(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		k_assert_eq(v.size, 16);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetVector(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXVECTOR4*)&dev->_vdeb.propv[2]);
	}

	static void PropVec3(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT4);
		k_assert_eq(v.size, 16);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetVector(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXVECTOR4*)&dev->_vdeb.propv[3]);
	}

	static void PropMat0(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_vdeb.propm[0].ToPointer());
	}

	static void PropMat1(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_vdeb.propm[1].ToPointer());
	}

	static void PropMat2(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_vdeb.propm[2].ToPointer());
	}

	static void PropMat3(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.data, QGSHDD_FLOAT16);
		k_assert_eq(v.size, 64);
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrix(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_vdeb.propm[3].ToPointer());
	}

	static void MatPalette(DX9Device* dev, kpointer data, const qgShaderVar& v)
	{
		ID3DXConstantTable* ctbl = (ID3DXConstantTable*)v.ptr;
		ctbl->SetMatrixArray(dev->GetDevice(), (D3DXHANDLE)v.offset, (D3DXMATRIX*)dev->_vprn.wb_pcb, dev->_vprn.wb_cnt);
	}

public:
	static qgShaderAutoInfo Autos[QGSHDA_MAX_VALUE];
};

// 목록
#define SV(m,t)	{0, "s" K_STRING(m), t, (qgShaderAutoFunc)&DX9ShaderWorker::m}
qgShaderAutoInfo DX9ShaderWorker::Autos[] =
{
	SV(OrthoProj, QGSHDA_ORTHO_PROJ),
	SV(World, QGSHDA_WORLD),
	SV(View, QGSHDA_VIEW),
	SV(Proj, QGSHDA_PROJ),
	SV(ViewProj, QGSHDA_VIEWPROJ),
	SV(InvView, QGSHDA_INVVIEW),
	SV(WorldViewProj, QGSHDA_WORLDVIEWPROJ),
	SV(Tex0, QGSHDA_TEX0),
	SV(Tex1, QGSHDA_TEX1),
	SV(Tex2, QGSHDA_TEX2),
	SV(Tex3, QGSHDA_TEX3),
	SV(PropVec0, QGSHDA_PROPVEC0),
	SV(PropVec1, QGSHDA_PROPVEC1),
	SV(PropVec2, QGSHDA_PROPVEC2),
	SV(PropVec3, QGSHDA_PROPVEC3),
	SV(PropMat0, QGSHDA_PROPMAT0),
	SV(PropMat1, QGSHDA_PROPMAT1),
	SV(PropMat2, QGSHDA_PROPMAT2),
	SV(PropMat3, QGSHDA_PROPMAT3),
	SV(MatPalette, QGSHDA_MATPALETTE),
};
#undef SV

//
void DX9Shader::_InitAutoFunc()
{
	for (ksize_t i = 0; i < K_COUNTOF(DX9ShaderWorker::Autos); i++)
		DX9ShaderWorker::Autos[i].hash = k_strihash(DX9ShaderWorker::Autos[i].name);
}

//
kpointer DX9Shader::_MatchAutoFunc(qgShaderVar& v)
{
	for (ksize_t i = 0; i < K_COUNTOF(DX9ShaderWorker::Autos); i++)
	{
		if (v.hash == DX9ShaderWorker::Autos[i].hash &&
			v.name.CompareTo(DX9ShaderWorker::Autos[i].name, true) == 0)
		{
			// 찾음
			v.role = QGSHDR_AUTO;
			v.intr = DX9ShaderWorker::Autos[i].func;

			return NULL;
		}
	}

	// 없음
	v.role = QGSHDR_MANUAL;
	v.intr = NULL;

	return NULL;
}

#endif	// _QG_USE_DX9
